package pers.qianyu.module.image.service.impl;

import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pers.qianyu.module.core.comm.Pagination;
import pers.qianyu.module.core.domain.image.converter.ImageConverter;
import pers.qianyu.module.core.domain.image.dto.ImageCollectionDTO;
import pers.qianyu.module.core.domain.image.dto.NoticeDTO;
import pers.qianyu.module.core.domain.image.entity.ImageCollectionPO;
import pers.qianyu.module.core.domain.image.entity.ImagePO;
import pers.qianyu.module.core.domain.image.enums.NoticeTypeEnum;
import pers.qianyu.module.core.domain.image.query.ImageCollectionQuery;
import pers.qianyu.module.core.domain.image.vo.ImageVO;
import pers.qianyu.module.image.dao.ImageCollectionDao;
import pers.qianyu.module.image.dao.ImageDao;
import pers.qianyu.module.image.exception.ImageErrorCodeEnum;
import pers.qianyu.module.image.exception.ImageException;
import pers.qianyu.module.image.listener.events.CollectImageEvent;
import pers.qianyu.module.image.service.ImageCollectionService;
import pers.qianyu.module.image.service.ImageLabelService;
import pers.qianyu.module.image.service.NoticeService;
import pers.qianyu.module.system.listener.events.UserInfoIdsUpdatedEvent;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author mizzle rain
 * @date 2021-02-09 11:09
 */
@Service("imageCollectionService")
public class ImageCollectionServiceImpl implements ImageCollectionService {
    @Autowired
    private ImageDao imageDao;
    @Autowired
    private ImageCollectionDao imageCollectionDao;
    @Autowired
    private ImageConverter imageConverter;
    @Autowired
    private NoticeService noticeService;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private ImageLabelService imageLabelService;

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void insert(ImageCollectionDTO imageCollectionDTO) {
        // 更新收藏数
        ImagePO imagePO = imageDao.selectById(imageCollectionDTO.getImageId());
        if (Objects.isNull(imagePO)) {
            throw new ImageException(ImageErrorCodeEnum.INVALID_IMAGE_ID);
        }
        imagePO.setFavorites(imagePO.getFavorites() + 1);
        imageDao.updateById(imagePO);
        // 插入记录
        ImageCollectionPO po = new ImageCollectionPO();
        po.setImageId(imageCollectionDTO.getImageId());
        po.setCreatedBy(imageCollectionDTO.getCreatedBy());
        po.setUpdatedBy(po.getCreatedBy());
        po.setCreateTime(System.currentTimeMillis());
        po.setUpdateTime(po.getCreateTime());
        imageCollectionDao.insert(po);

        // 插入一条通知
        applicationContext.publishEvent(new CollectImageEvent(this, imagePO, imageCollectionDTO.getCreatedBy(), NoticeTypeEnum.COLLECTED));
        // 更新 Redis 用户信息（一定要在最后更新 Redis，否则事务回滚的话会读取到错误结果）
        List<Long> ids = findImageIdsByUserId(imageCollectionDTO.getCreatedBy());
        applicationContext.publishEvent(new UserInfoIdsUpdatedEvent(this,
                ids, imageCollectionDTO.getCreatedBy(), UserInfoIdsUpdatedEvent.Type.IMAGE_COLLECTION_IDS_UPDATED));
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void delete(ImageCollectionDTO imageCollectionDTO) {
        // 更新收藏数
        ImagePO imagePO = imageDao.selectById(imageCollectionDTO.getImageId());
        if (Objects.isNull(imagePO)) {
            throw new ImageException(ImageErrorCodeEnum.INVALID_IMAGE_ID);
        }
        imagePO.setFavorites(imagePO.getFavorites() - 1);
        imageDao.updateById(imagePO);
        // 删除记录
        ImageCollectionPO po = new ImageCollectionPO();
        po.setImageId(imageCollectionDTO.getImageId());
        po.setUpdatedBy(po.getCreatedBy());
        QueryWrapper<ImageCollectionPO> wrapper = new QueryWrapper<>(po);
        imageCollectionDao.delete(wrapper);

        // 如果通知的状态为未读，则删除对应的通知
        NoticeDTO noticeDTO = new NoticeDTO();
        noticeDTO.setCreatedBy(imageCollectionDTO.getCreatedBy());
        noticeDTO.setType(NoticeTypeEnum.COLLECTED.getCode());
        noticeDTO.setTargetId(imagePO.getId());
        noticeDTO.setOwnerId(imagePO.getCreatedBy());
        noticeService.delete(noticeDTO);
        // 更新 Redis 用户信息（一定要在最后更新 Redis，否则事务回滚的话会读取到错误结果）
        List<Long> ids = findImageIdsByUserId(imageCollectionDTO.getCreatedBy());
        applicationContext.publishEvent(new UserInfoIdsUpdatedEvent(this, ids,
                imageCollectionDTO.getCreatedBy(), UserInfoIdsUpdatedEvent.Type.IMAGE_COLLECTION_IDS_UPDATED));
    }

    @Override
    public List<Long> findImageIdsByUserId(Long userId) {
        QueryWrapper<ImageCollectionPO> wrapper = new QueryWrapper<>();
        wrapper.select("image_id").eq("created_by", userId);
        List<ImageCollectionPO> imageLikePOS = imageCollectionDao.selectList(wrapper);
        return Optional.ofNullable(imageLikePOS)
                .map(List::stream)
                .orElseGet(Stream::empty)
                .map(ImageCollectionPO::getImageId)
                .collect(Collectors.toList());
    }

    @Override
    public Pagination<ImageVO> queryImage(ImageCollectionQuery imageCollectionQuery) {
        Page<ImageCollectionPO> page = new Page<>(imageCollectionQuery.getPage(), imageCollectionQuery.getLimit());
        QueryWrapper<ImageCollectionPO> wrapper = new QueryWrapper<>();
        wrapper.eq("created_by", imageCollectionQuery.getCreatedBy()).orderByDesc("create_time");
        Page<ImageCollectionPO> imageCollectionPOPage = imageCollectionDao.selectPage(page, wrapper);
        List<Long> imageIds = Optional.ofNullable(imageCollectionPOPage.getRecords())
                .map(List::stream)
                .orElseGet(Stream::empty)
                .map(ImageCollectionPO::getImageId)
                .collect(Collectors.toList());
        if (imageIds.isEmpty()) {
            return Pagination.emptyPage(imageCollectionQuery.getPage(), imageCollectionQuery.getLimit());
        }
        List<ImageVO> imageVOS = Optional.ofNullable(imageDao.selectBatchIds(imageIds))
                .map(List::stream)
                .orElseGet(Stream::empty)
                .map(i -> {
                    Set<String> set = imageLabelService.getLabelsByImageId(i.getId());
                    return imageConverter.convertToVO(i, ArrayUtil.toArray(set, String.class));
                })
                .collect(Collectors.toList());

        Pagination<ImageVO> pagination = new Pagination<>();
        pagination.setCurrent(imageCollectionPOPage.getCurrent());
        pagination.setSize(imageCollectionPOPage.getSize());
        pagination.setTotal(imageCollectionPOPage.getTotal());
        pagination.setRecords(imageVOS);
        return pagination;
    }
}
